/*
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package utopiaintelpackage;

import interfaces.TextHandler;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import main.Bot;
import main.CommonMethods;
import main.Main;

import org.apache.log4j.Logger;
import org.jibble.pircbot.Colors;

import output.IRCOutput;
import output.OutputTypeEnum;
import utopiaintelpackage.database.KDtoDB;
import utopiaintelpackage.database.SoMtoDB;
import utopiaintelpackage.database.SoStoDB;
import utopiaintelpackage.database.SoTtoDB;
import utopiaintelpackage.database.SurveytoDB;
import utopiaintelpackage.kd.KD;
import utopiaintelpackage.kd.KDParser;
import utopiaintelpackage.som.SoM;
import utopiaintelpackage.som.SoMParser;
import utopiaintelpackage.sos.SoS;
import utopiaintelpackage.sos.SoSParser;
import utopiaintelpackage.sot.SoT;
import utopiaintelpackage.sot.SoTParser;
import utopiaintelpackage.survey.Survey;
import utopiaintelpackage.survey.SurveyParser;
import exceptions.IntelParsingException;

/**
 * This class acts as an entry point for intel parsing
 * 
 * 
 */
public enum BaseParser implements TextHandler {
   INSTANCE;

   private final Map<String, AbstractIntel> map;

   private BaseParser() {
	  map = new HashMap<String, AbstractIntel>();
   }

   private SoT getSoT(final String name) {
	  return SoTtoDB.getSoT(name);
   }

   @Override
   public boolean handleText(final String message, final String channel, final String sender, final IRCOutput out) {
	  if (message.matches(".*Current kingdom is .* \\([0-9]{1,2}(:)[0-9]{1,2}\\).*")) {
		 KD kd = new KD();
		 map.put(sender, kd);
	  } else if (message.matches(".* \\([0-9]{1,2}(:)[0-9]{1,2}\\) Kingdom Analysis")) {
		 KD kd = new KD();
		 map.put(sender, kd);
	  }

	  boolean wasIntel = false;
	  if (map.containsKey(sender)) {
		 String type = map.get(sender).getTypeOfIntel();
		 try {
			if ("KD".equals(type)) {
			   KDParser kdparser = new KDParser();
			   wasIntel = kdparser.parseKD(message, (KD) map.get(sender));
			   if (((KD) map.get(sender)).isComplete()
					 && (message.matches(".*?Version: [0-9]{1,}") || message.matches(((KD) map.get(sender)).getAmountOfProvs() + "\\. .*"))) {
				  KD kd = (KD) map.get(sender);
				  map.remove(sender);
				  out.addOutput(saveKD(kd));
				  out.setDelivery(OutputTypeEnum.SEND_IN_PM);
				  out.setTarget(sender);
			   }
			}
		 } catch (final IntelParsingException e) {
			Logger.getLogger("Error").error("", e);
		 }
	  }
	  return wasIntel;
   }

   /**
    * This method parses and saves Angel formatted intel received from Forum Agent
    * 
    * @param intel
    * @param sender
    * @return a list with strings to respond to forum agent
    */
   public List<String> parseIntel(final List<String> intel, final String sender) {
	  String temp = "";
	  for (final String s : intel) {
		 temp += s + (char) 30;
	  }
	  String[] split = temp.split(String.valueOf((char) 29));
	  int counter = 0;
	  List<String> out = new LinkedList<String>();
	  for (final String compare : split) { // loop through the different intel items
		 List<String> newIntel = new ArrayList<String>(Arrays.asList(compare.split(String.valueOf((char) 30))));
		 // newIntel splits the intel item into lines
		 if (compare.matches(".*Current kingdom is .*\\([0-9]{1,2}(:)[0-9]{1,2}\\).*")) { // intel is a kd page
			KD kd = new KD();
			KDParser kdparser = new KDParser();
			try {
			   for (String in : newIntel) {
				  kdparser.parseKD(in, kd);
			   }

			   if (kd.isComplete()) {
				  out.add("+" + counter);
				  String outcome = saveKD(kd);
				  if ("KD added".equals(outcome) && !kd.getLoc().equals("(" + Main.getBotSettings().getKdLoc() + ")")
						&& !kd.getLoc().equals(Main.getBotSettings().getKdLoc())) {
					 Bot.getInstance().sendMessage(Main.getBotSettings().getMainChannel(),
						   Colors.OLIVE + "KD page of " + Colors.BLUE + kd.getLoc() + " added");
				  }
			   } else {
				  out.add("-" + counter + " KD page failed");
			   }
			} catch (IntelParsingException e) {
			   Logger.getLogger("Error").error("", e);
			}
		 } else if (compare.matches(".*The Province of .* \\([0-9]{1,2}(:)[0-9]{1,2}\\).*")) { // intel is a SoT
			try {
			   SoT sot = new SoT();
			   SoTParser sotparser = new SoTParser();
			   for (final String s : newIntel) {
				  sotparser.parseFormattedSoT(s, sot);
			   }
			   if (sot.getUpdated() == null) {
				  sot.setUpdated(new Date(Calendar.getInstance().getTimeInMillis()));
			   }
			   for (int i = 0; i < newIntel.size(); ++i) {
				  if (newIntel.get(i).matches("\\*\\* Export Line .* \\*\\*")) {
					 String export = "";
					 for (int j = i + 1; j < newIntel.size(); ++j) {
						export += newIntel.get(j);
					 }
					 sot.setExportLine(export);
				  }
			   }
			   sot.setSavedBy(sender);
			   if (sot.isComplete()) {
				  out.add("+" + counter);
				  if (sot.getNw() == 0) {
					 SoT temp1 = getSoT(sot.getName());
					 if (temp1 != null) {
						sot.setNw(temp1.getNw());
					 }
				  }
				  String outcome = saveSoT(sot);
				  if ("SoT added".equals(outcome) && !sot.getKdloc().equals("(" + Main.getBotSettings().getKdLoc() + ")")
						&& !sot.getKdloc().equals(Main.getBotSettings().getKdLoc())) {
					 CommonMethods.addAction(sender, "SoT");
					 Bot.getInstance().sendMessage(
						   Main.getBotSettings().getMainChannel(),
						   Colors.OLIVE + "SoT of " + Colors.BLUE + sot.getName() + sot.getKdloc() + Colors.OLIVE + " added by "
								 + Colors.NORMAL + "o" + Colors.RED + sot.getSavedBy() + Colors.NORMAL + "o");
				  }
			   } else {
				  out.add("-" + counter + " SoT failed");
			   }
			} catch (final IntelParsingException e) {
			   Logger.getLogger("Error").error("", e);
			}
		 } else if (compare.matches(".*Science (Intelligence|Intel) on .*\\([0-9]{1,2}(:)[0-9]{1,2}\\).*")) { // intel is a SoS
			try {
			   SoS sos = new SoS(CommonMethods.listSciences().size());
			   SoSParser sosparser = new SoSParser();
			   for (final String s : newIntel) {
				  sosparser.parseFormattedSoS(s, sos);
			   }
			   sos.setUpdated(new Date(Calendar.getInstance().getTimeInMillis()));
			   for (int i = 0; i < newIntel.size(); ++i) {
				  if (newIntel.get(i).matches("\\*\\* Export Line .* \\*\\*")) {
					 String export = "";
					 for (int j = i + 1; j < newIntel.size(); ++j) {
						export += newIntel.get(j);
					 }
					 sos.setExportLine(export);
				  }
			   }
			   sos.setSavedBy(sender);
			   if (sos.isComplete()) {
				  out.add("+" + counter);
				  String outcome = saveSoS(sos);
				  if ("SoS added".equals(outcome) && !sos.getKdloc().equals("(" + Main.getBotSettings().getKdLoc() + ")")
						&& !sos.getKdloc().equals(Main.getBotSettings().getKdLoc())) {
					 CommonMethods.addAction(sender, "SoS");
					 Bot.getInstance().sendMessage(
						   Main.getBotSettings().getMainChannel(),
						   Colors.OLIVE + "SoS of " + Colors.BLUE + sos.getName() + sos.getKdloc() + Colors.OLIVE + " added by "
								 + Colors.NORMAL + "o" + Colors.RED + sos.getSavedBy() + Colors.NORMAL + "o");
				  }
			   } else {
				  out.add("-" + counter + " SoS failed");
			   }
			} catch (final IntelParsingException e) {
			   Logger.getLogger("Error").error("", e);
			}
		 } else if (compare.matches(".*Buildings Report of .*\\([0-9]{1,2}(:)[0-9]{1,2}\\).*")// intel is a Survey
			   || compare.matches(".*Survey / Buildings Report Information.*")) {
			if (compare.matches(".*Survey / Buildings Report Information.*") && "".equals(CommonMethods.getProv(sender))) {
			   out.add("-0 You have no province");// Angel formatted self surveys for some reason don't include prov name
			   // If the user isn't registered to a bot, there is no way to know which prov to add for, so it's rejected
			   // instead
			} else {
			   try {
				  Survey survey = new Survey();
				  SurveyParser surveyparser = new SurveyParser();
				  for (final String s : newIntel) {
					 surveyparser.parseFormattedSurvey(s, survey);
				  }
				  survey.setUpdated(new Date(Calendar.getInstance().getTimeInMillis()));
				  if (survey.getName().equals("")) {
					 survey.setName(CommonMethods.getProv(sender));
					 survey.setKdloc("(" + Main.getBotSettings().getKdLoc() + ")");
				  }
				  for (int i = 0; i < newIntel.size(); ++i) {
					 if (newIntel.get(i).matches("\\*\\* Export Line .* \\*\\*")) {
						String export = "";
						for (int j = i + 1; j < newIntel.size(); ++j) {
						   export += newIntel.get(j);
						}
						survey.setExportLine(export);
					 }
				  }
				  survey.setSavedBy(sender);
				  if (survey.isComplete()) {
					 out.add("+" + counter);
					 final String outcome = saveSurvey(survey);
					 if ("Survey added".equals(outcome)
						   && !survey.getKdloc().equals("(" + Main.getBotSettings().getKdLoc() + ")")
						   && !survey.getKdloc().equals(Main.getBotSettings().getKdLoc())) {
						CommonMethods.addAction(sender, "Survey");
						Bot.getInstance()
							  .sendMessage(
									Main.getBotSettings().getMainChannel(),
									Colors.OLIVE + "Survey of " + Colors.BLUE + survey.getName() + survey.getKdloc()
										  + Colors.OLIVE + " added by " + Colors.NORMAL + "o" + Colors.RED + survey.getSavedBy()
										  + Colors.NORMAL + "o");
					 }
				  } else {
					 out.add("-" + counter + " Survey failed");
				  }
			   } catch (final IntelParsingException e) {
				  Logger.getLogger("Error").error("", e);
			   }
			}
		 } else if (compare.matches(".*Military (Intelligence|Intel) on .*\\([0-9]{1,2}(:)[0-9]{1,2}\\).*")) {// intel is a SoM
			try {
			   SoM som = new SoM();
			   SoMParser somparser = new SoMParser();
			   newIntel.add("** Finished **");
			   for (final String s : newIntel) {
				  somparser.parseFormattedSoM(s, som);
			   }
			   som.setUpdated(new Date(Calendar.getInstance().getTimeInMillis()));
			   if (som.getName().equals("")) {
				  som.setName(CommonMethods.getProv(sender));
				  som.setKdloc("(" + Main.getBotSettings().getKdLoc() + ")");
			   }
			   for (int i = 0; i < newIntel.size(); ++i) {
				  if (newIntel.get(i).matches("\\*\\* Export Line .* \\*\\*")) {
					 String export = "";
					 for (int j = i + 1; j < newIntel.size(); ++j) {
						if (newIntel.get(j).equals("** Finished **")) {
						   break;
						}
						export += newIntel.get(j);
					 }
					 som.setExportLine(export);
				  }
			   }
			   som.setSavedBy(sender);
			   if (som.isComplete()) {
				  out.add("+" + counter);
				  String outcome = saveSoM(som);
				  if ("SoM added".equals(outcome) && !som.getKdloc().equals("(" + Main.getBotSettings().getKdLoc() + ")")
						&& !som.getKdloc().equals(Main.getBotSettings().getKdLoc())) {
					 CommonMethods.addAction(sender, "SoM");
					 Bot.getInstance().sendMessage(
						   Main.getBotSettings().getMainChannel(),
						   Colors.OLIVE + "SoM of " + Colors.BLUE + som.getName() + som.getKdloc() + Colors.OLIVE + " added by "
								 + Colors.NORMAL + "o" + Colors.RED + som.getSavedBy() + Colors.NORMAL + "o");
				  }
			   } else {
				  out.add("-" + counter + " SoM failed");
			   }
			} catch (final IntelParsingException e) {
			   Logger.getLogger("Error").error("", e);
			}
		 } else {
			out.add("+" + counter);
		 }
		 ++counter;
	  }
	  return out;
   }

   private String saveKD(final KD kd) {
	  return KDtoDB.saveKD(kd);
   }

   private String saveSoM(final SoM som) {
	  return SoMtoDB.saveSoM(som);
   }

   private String saveSoS(final SoS sos) {
	  return SoStoDB.saveSoS(sos);
   }

   private String saveSoT(final SoT sot) {
	  return SoTtoDB.saveSoT(sot);
   }

   private String saveSurvey(final Survey survey) {
	  return SurveytoDB.saveSurvey(survey);
   }

   @Override
   public boolean willHandleText(final String message, final String channel, final String sender) {
	  return true;
   }
}
